package org.ovirt.jenkins;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.ovirt.jenkins.model.CPU;
import org.ovirt.jenkins.model.GuestInfo;
import org.ovirt.jenkins.model.IP;
import org.ovirt.jenkins.model.OS;
import org.ovirt.jenkins.model.VM;
import org.ovirt.jenkins.model.VMs;
import org.ovirt.jenkins.model.VmPool;
import org.ovirt.jenkins.model.VmPools;
import org.ovirt.jenkins.model.VmStatus;

public class OVirtService {

	public OVirtService(final String restApiUrl, final String apiUser,
			final String apiPass) {
		super();
		this.restApiUrl = restApiUrl;
		this.apiPass = apiPass;
		this.apiUser = apiUser;
	}

	private synchronized JAXBContext getContext() throws JAXBException {
		if(context == null) {
			context = JAXBContext.newInstance(VM.class, VMs.class, VmPool.class,
					VmPools.class, OS.class, CPU.class, VmStatus.class,
					GuestInfo.class, IP.class);
		}
		return context;
	}

	private synchronized HttpClient getClient() {
		if(httpClient == null) {
			httpClient = new HttpClient();
			httpClient.getParams().setAuthenticationPreemptive(true);
			httpClient.getState().setCredentials(AuthScope.ANY,
					new UsernamePasswordCredentials(apiUser, apiPass));
		}
		return httpClient;
	}

	final String restApiUrl;
	final String apiUser;
	final String apiPass;
	private transient HttpClient httpClient;
	private transient JAXBContext context;

	public List<VM> listVms(final String pool, final String label)
			throws IOException {
		try {
			final String poolId = getPoolId(pool);
			if (poolId == null) {
				// pool does not exist!
				return new ArrayList<VM>();
			}
			final VMs vms = (VMs) get("vms");
			final ArrayList<VM> ret = new ArrayList<VM>();
			for (final VM vm : vms.getVms()) {
				if (vm.getPool() != null
						&& StringUtils.equals(poolId, vm.getPool().getId())
						&& checkLabel(label, vm)) {
					ret.add(vm);
				}
			}
			return ret;
		} catch (Exception e) {
			throw new IOException(e.getMessage(), e);
		}
	}

	/**
	 * Check if the label is there on a VM.
	 * 
	 * @param label
	 * @param vm
	 * @return
	 * @throws IOException
	 */
	private boolean checkLabel(final String label, final VM vm)
			throws IOException {
		try {
			// if not label needed, then this label is there
			if (label == null) {
				return true;
			}

			// final List<Tag> tags = null;//clientRequestFactory.get(
			// // restApiUrl + "/vms/" + vm.getId() + "/tags", Tags.class)
			// // .getTags();
			// for (final Tag tag : tags) {
			// if (StringUtils.equals(label, tag.getName())) {
			// return true;
			// }
			// }
			return false;
		} catch (Exception e) {
			throw new IOException(e.getMessage(), e);
		}
	}

	private String getPoolId(final String pool) throws Exception {
		VmPools pools = (VmPools) get("vmpools");
		List<VmPool> vmPools = pools.getVmPools();
		for (VmPool vmPool : vmPools) {
			if (ObjectUtils.equals(pool, vmPool.getName())) {
				return ObjectUtils.toString(vmPool.getId());
			}
		}
		return null;
	}

	public void launch(String vmid) throws IOException {
		try {
			post("vms/" + vmid + "/start");
		} catch (JAXBException e) {
			throw new IOException(e.getMessage(), e);
		}
	}

	public void shutdown(String vmid) throws IOException {
		try {
			post("vms/" + vmid + "/shutdown");
		} catch (JAXBException e) {
			throw new IOException(e.getMessage(), e);
		}
	}

	private Object get(String relUrl) throws IOException, JAXBException {
		final GetMethod get = new GetMethod(restApiUrl
				+ (StringUtils.endsWith(restApiUrl, "/") ? "" : "/") + relUrl);
		get.addRequestHeader("Accept", "application/xml");

		getClient().executeMethod(get);
		return getContext().createUnmarshaller().unmarshal(
				get.getResponseBodyAsStream());
	}

	private int post(String relUrl) throws IOException, JAXBException {
		final PostMethod post = new PostMethod(restApiUrl
				+ (StringUtils.endsWith(restApiUrl, "/") ? "" : "/") + relUrl);
		post.addRequestHeader("Accept", "application/xml");
		post.setRequestEntity(new StringRequestEntity("<action />",
				"application/xml", "utf-8"));
		getClient().executeMethod(post);
		return post.getStatusCode();
	}

	public VM getVm(String id) throws IOException {
		try {
			return (VM) get("vms/" + id);
		} catch (JAXBException e) {
			throw new IOException(e.getMessage(), e);
		}
	}

}
